'''
    @project: Masdevallia
    @author: Austin Baker
    @created: 2/22/2014
    @license: http://opensource.org/licenses/mit-license.php

    The MIT License (MIT)

    Copyright (c) 2014 Austin Baker
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    
    ------
    
    About: Creates pypredef files for autocompletion.
    
    Usage: The modules added to the list MODULES are processed and converted
            to pypredef files.
            
            To add more modules to the list you must first import them and then
            add the module to the list.
            
            To remove modules from the list just delete the list entry.
'''

'''
============================================================
--->>   IMPORT MODULES
============================================================
'''
import inspect, types, os, sys, importlib
import masdevallia.settings.PathConstants as PathConstants


'''
============================================================
--->>   EXECUTE
============================================================
'''
def process_modules_all():
    '''
    Process all modules for the package.
    '''
    modules = ['masdevallia.meta.ArmMeta',
               'masdevallia.meta.BaseMeta',
               'masdevallia.meta.RigMeta',
               'masdevallia.meta.RootMeta',
               'masdevallia.modeling.Curves',
               'masdevallia.nodes.Node',
               'masdevallia.rigging.modules.RigModule',
               'masdevallia.rigging.modules.RigModuleMarker',
               'masdevallia.rigging.modules.arms.BasicArm',
               'masdevallia.rigging.modules.roots.BasicRoot',
               'masdevallia.rigging.Controls',
               'masdevallia.rigging.CurveControl',
               'masdevallia.rigging.PolygonRigControl',
               'masdevallia.rigging.RigJoint',
               'masdevallia.rigging.SurfaceRigControl',
               'masdevallia.settings.PathConstants',
               'masdevallia.utility.Colors',
               'masdevallia.utility.Dag',
               'masdevallia.utility.FileSystem',
               'masdevallia.utility.General',
               'masdevallia.utility.Matrix',
               'masdevallia.utility.MScriptUtility',
               'masdevallia.utility.RigControls',
               'masdevallia.utility.Selection',
               'masdevallia.utility.Vector',
               'masdevallia.utility.WindowLauncher',
               'masdevallia.utility.WindowUtility',
               'masdevallia.utility.XML',
               'masdevallia.windows.AnimationLibrary',
               'masdevallia.windows.BuildModules',
               'masdevallia.windows.BuildParts',
               'masdevallia.windows.BuildRigs',
               'masdevallia.windows.CharacterMap',
               'masdevallia.windows.Dialogs',
               'masdevallia.windows.EditModules',
               'masdevallia.windows.InfoTile',
               'masdevallia.windows.ModuleInfoTile',
               'masdevallia.windows.RigLibrary',
               'masdevallia.windows.RigTools']
    
    for module_name in modules:
        module = importlib.import_module( module_name )
        createPredef( inObj=module )
        
def process_modules_list( inModules ):
    '''
    Process a list of modules from 1 to ~.
    
    @param inModules: List of Str. Each item is a full module name,
        ex. masdevallia.modeling.Curves.
    '''
    for module_name in inModules:
        module = importlib.import_module( module_name )
        createPredef( inObj=module )
    

'''
============================================================
--->>   PROCESSING FUNCTIONS
============================================================
'''
def createStubHeader( inModule ):
    '''
    Creates string containing information about the module. Uses as a header
    for the pypredef file.
   
    @type inModule: Module.
    @return: String
    '''
    stub = "'''\n"    
    stub += 'Stub for module: {0}\n'.format( inModule.__name__ )
    stub += 'Source file: {0}\n\n'.format( inModule.__file__ )
    stub += inModule.__doc__    
    stub += "'''\n\n\n"   
    return stub

def getArgs( inMethod ):
    '''
    Gets all the arguments for the pass in method.
   
    @type inMethod: Method.
    @return: String.
    '''
    args_final = []
    line = ' '
    
    try:
        args, varargs, kwargs, defaults = inspect.getargspec( inMethod )

        if args:
            if defaults:
                keywords = args[ -len(defaults): ]
                args_final = zip( keywords, defaults )
                
                nonkeywords = args[ :-len(defaults) ]
                temp_list = []
                
                for arg in nonkeywords:
                    temp_list.append( ( arg, 'no default' ) )
                
                args_final = temp_list + args_final
                
            else:
                for arg in args:
                    args_final.append( ( arg, 'no default' ) )
                    
        line_iter = 1
        for i in args_final:
            if line_iter == len( args_final ):
                if i[1] == 'no default':
                    line += '{0} '.format( i[0] )
                    
                else:
                    if isinstance( i[1], str ):
                        if i[1] == '':
                            str_value = "''"
                        
                        else:
                            str_value = "'{0}'".format( i[1] )
                            
                        line += '{0}={1} '.format( i[0], str_value )
                    
                    else:
                        line += '{0}={1} '.format( i[0], i[1] )
                    
            else:
                if i[1] == 'no default':
                    line += '{0}, '.format( i[0] )
                    
                else:
                    if isinstance( i[1], str ):
                        if i[1] == '':
                            str_value = "''"
                            
                        else:
                            str_value = "'{0}'".format( i[1] )
                            
                        line += '{0}={1}, '.format( i[0], str_value )
                        
                    else:
                        line += '{0}={1}, '.format( i[0], i[1] )
                    
            line_iter += 1
        
    except Exception, e:
           
        exc_type, exc_obj, exc_tb = sys.exc_info()
        func_name = os.path.split( exc_tb.tb_frame.f_code.co_filename )[1]
        print 'ERROR line {0} in {1} of type {2}'.format( exc_tb.tb_lineno, func_name, exc_type )
        print '    {0}'.format( exc_obj )
        
    return line

def createPredef( inObj=None ):
    '''
    Processes the passed in module turning it into a pypredef file.
    
    @param inObj: Python Live Object. Typically a Module.
    '''
    try:
        print 'Building predef for <{0}>'.format( inObj.__name__ )
        
        # Open the pypredef file. Create the file if it doesn't already exist.
        if not os.path.exists( PathConstants.PATH_MASDEVALLIA_PYPREDEF ):
            os.makedirs( PathConstants.PATH_MASDEVALLIA_PYPREDEF )
            
        predef_file = open( '{0}\\{1}.pypredef'.format( PathConstants.PATH_MASDEVALLIA_PYPREDEF, inObj.__name__ ), 'w+' )
        
        # Do the head for the predef.
        stub = createStubHeader( inObj )
        predef_file.write( stub )
        
        # Loop through the members of the module.
        for name, data in inspect.getmembers( inObj ):
            # Filter out the __builtins__.
            #if name == '__builtins__': continue
            if name.startswith( '__' ):
                continue
            
            # Filters out any imported modules used in the passed in Object.
            elif isinstance( data, types.ModuleType ):
                continue        
            
            # Write classes.
            elif inspect.isclass( data ):
                # Write the class declaration.
                class_bases = data.__bases__
                class_bases_str= ''
                
                if class_bases:
                    for base in class_bases:
                        class_bases_str += base.__name__
                        
                class_def = 'class {0}( {1} ):\n'.format( data.__name__, class_bases_str )
                predef_file.write( class_def )
                
                if data.__doc__:
                    class_doc = "    '''"
                    class_doc += data.__doc__
                    class_doc += "'''\n"
                    predef_file.write( class_doc )
                
                for member_name, member_data in inspect.getmembers( data, inspect.ismethod ):
                    method_args = getArgs( member_data )
                    
                    if method_args:
                        class_method = '    def {0}({1})\n'.format( member_data.__name__, method_args )
                        
                    else:
                        class_method = '    def {0}()\n'.format( member_data.__name__ )
                        
                    if member_data.__doc__:
                        class_method += "        '''"
                        class_method += '{0}'.format( member_data.__doc__ )
                        class_method += "'''\n"
                        
                    class_method += '        pass\n\n'
                    predef_file.write( class_method )
                    
            # Write Module level functions.
            elif inspect.isfunction( data ):
                method_args = getArgs( data )
                
                if method_args:
                    method_def = 'def {0}({1})\n'.format( data.__name__, method_args )
                    
                else:
                    method_def = 'def {0}()\n'.format( data.__name__ )
                    
                if data.__doc__:
                    method_def += "    '''"
                    method_def += '{0}'.format( data.__doc__ )
                    method_def += "'''\n"
                    
                method_def += '    pass\n\n'
                predef_file.write( method_def )     
            
            # Write global variables.
            
            # Need to figure out a way to get global variables that are instances
            # of a class. None of the following worked...
            #elif hasattr( data, '__init__' ):
            #elif type( data ) is data.__class__:
            #elif type( data ) is types.InstanceType:
            #elif hasattr( data, '__name__' ):
            #elif type( data) is types.MethodType:
            elif isinstance( data, str ):
                global_var = '{0}=str\n\n'.format( name )
                predef_file.write( global_var )
                
            elif isinstance( data, int ):
                global_var = '{0}=int\n\n'.format( name )
                predef_file.write( global_var )
                             
            elif isinstance( data, long ):
                global_var = '{0}=long\n\n'.format( name )
                predef_file.write( global_var )
                
            elif isinstance( data, float ):
                global_var = '{0}=float\n\n'.format( name )
                predef_file.write( global_var )
                
            elif isinstance( data, complex):
                global_var = '{0}=complex\n\n'.format( name )
                predef_file.write( global_var )
            
            elif isinstance( data, tuple ):
                global_var = '{0}=tuple\n\n'.format( name )
                predef_file.write( global_var )

            elif isinstance( data, list ):
                global_var = '{0}=list\n\n'.format( name )
                predef_file.write( global_var )
                
            elif isinstance( data, dict ):
                global_var = '{0}=dict\n\n'.format( name )
                predef_file.write( global_var )
                
                
            elif isinstance( data, set ):
                global_var = '{0}=set\n\n'.format( name )
                predef_file.write( global_var )
                
            elif isinstance( data, bool ):
                global_var = '{0}=bool\n\n'.format( name )
                predef_file.write( global_var )
    
        predef_file.close()
        
    except Exception, e:
           
        exc_type, exc_obj, exc_tb = sys.exc_info()
        func_name = os.path.split( exc_tb.tb_frame.f_code.co_filename )[1]
        print 'ERROR line {0} in {1} of type {2}'.format( exc_tb.tb_lineno, func_name, exc_type )
        print '    {0}'.format( exc_obj )
        
'''
'''
process_modules_all()
#process_modules_list( ['masdevallia.utility.XML'] )